#include "laurin-module.h"
#include <sched.h>



/**
 *
 * Thread flow
 *
 *  start() -----> wait() -----> run() ----> wait() (with block condition)
 *
 *  if another start() call is made then
 *
 *  start() -----> wakeup()----> wait() (with block condition to false) ----> run() ---> wait() (with block condition)
 *
 */


/**
 *  the wakeup variable is necessary for the right synchronization.
 *  On wait(), thread is put to spleep. The wakeup variable and the wait condition variable govern this loop.
 */


LaurinModule::LaurinModule(const Glib::ustring& moduleName, LaurinModuleHandler* handler)
{
	if(!Glib::thread_supported())
		Glib::thread_init();

	_module_name = Glib::ustring(moduleName);
	_id = new Glib::Quark(moduleName);

	_thread = NULL;
	_created = false;			// thread not created

	_wakeup = false;
	_exit = false;
	_working = false;
	_stop_request = false;

	_handler = handler;
}


LaurinModule::~LaurinModule()
{
	// operation must be terminated on the subclass
	// so call join() is safe

	delete _id;


	if(_thread != NULL)
	{
		_exit = true;
		stop(false);
		wakeup();
		_thread->join();
	}

}


Glib::ustring LaurinModule::getErrorMessage() const
{
	return _errmess;
}


void LaurinModule::wait()
{
	sched_yield();
	bool rs = false;

	while(!_exit)
	{

		// wait for wakeup signal
		_mutex.lock();

		while(!_wakeup)
			_wait.wait(_mutex);

		_mutex.unlock();



		// do work
		if(!_exit)
		{
			_mutex.lock();
			_wakeup = false;
			_working = true;
			_stop_request = false;
			_mutex.unlock();

			rs = run();

			_mutex.lock();
			_working = false;
			_stop_wait.signal();
			_mutex.unlock();

			if(!_exit)
			{
				if(!rs)
					_handler->setError(_errmess);

				_handler->moduleWorkingComplete(!rs);
			}
		}
	}
}


void LaurinModule::wakeup()
{

	_mutex.lock();
	_wakeup = true;
	_wait.signal();
	_mutex.unlock();

	// now thread is running!
}


void LaurinModule::setActive()
{
	// thread creation is delayed until really necessary.
	// the _created variable represent the thread state.
	// if thread is already created we need only to wake up it, because it's waiting on wait().
	// the wakeup variable control the wait() method.

	if(!_created)
	{
		_created = true;
		_wakeup = true;

		// thread is joinable
		_thread = Glib::Thread::create(sigc::mem_fun(*this, &LaurinModule::wait), true);
	}
	else
		// it's alredy on wait()
		wakeup();
}


bool LaurinModule::start()
{
	if(isWorking())
	{
		_errmess = Glib::ustring("Module \"").append(_module_name).append("\" is alredy running");
		return false;
	}

	// create thread if not alredy created
	setActive();

	return true;
}


bool LaurinModule::stop(bool wait)
{

	if(!isWorking())
	{
		_errmess = Glib::ustring("Module \"").append(_module_name).append("\" is not running");
		return false;
	}


	_mutex.lock();
	_stop_request = true;
	_mutex.unlock();


	innerStop();


	if(wait)
	{
		// I can't lose the signal: _working is setted to false only when the _stop_wait signal is raised.
		// and signal is raised only inside a critical region controlled by _mutex.

		_mutex.lock();
		while(_working)
			_stop_wait.wait(_mutex);

		_mutex.unlock();
	}


	return true;
}


bool LaurinModule::isStopRequestPending()
{
	bool rs = false;
	_mutex.lock();
	rs = _stop_request;
	_mutex.unlock();

	return rs;
}


bool LaurinModule::isWorking()
{
	bool rs = false;
	_mutex.lock();
	rs = _working;
	_mutex.unlock();

	return rs;
}


Glib::ustring LaurinModule::getModuleName() const
{
	return _module_name;
}


bool LaurinModule::operator=(const LaurinModule& op)
{
	return (*_id) == *(op._id);

}


void LaurinModule::innerStop(){}
